package com.ruoyi.web.spring.zhs.spring;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;

import java.beans.Introspector;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author zhenghuisheng
 * @date 2024/10/28 23:00
 */
public class ZhsApplicationContext {

    private Class configClass;

    private static final String SINGLETON = "singleton";
    private Map<String,BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>();

    private Map<String, Object> singletonObjects = new HashMap<>();
    private List<BeanPostProcessor> beanPostProcessorList = new ArrayList<>();

    //配置上下文
    public ZhsApplicationContext(Class configClass){
        this.configClass = configClass;
        //扫描全部的实例，将每个实例注册成一个bean定义，然后存到三级缓存中
        //每个实例有对应的类型，比如说说是否懒加载，是否单例bean，是否原型bean
        scan(configClass);

        //注册并创建bean
        register(beanDefinitionMap);
    }

    private void scan(Class configClass) {
        if (configClass.isAnnotationPresent(ComponentScan.class)){
            //获取当前注解
            ComponentScan componentScan = (ComponentScan) configClass.getAnnotation(ComponentScan.class);
            //获取到扫描注解中的value值
            String name = componentScan.value();
            name = name.replace(".","/");
            //获取应用类加载器
            ClassLoader classLoader = ZhsApplicationContext.class.getClassLoader();
            //获取类加载器下面的全部url路径
            URL resource = classLoader.getResource(name);
            String url = resource.getFile();
            //以文件的形式加载全部的路径
            File file = new File(url);
            if (file.isDirectory()){
                for (File currentFile : file.listFiles()) {
                    String absolutePath = currentFile.getAbsolutePath();
                    //通过绝对路径获取到相对路径
                    absolutePath = absolutePath.substring(absolutePath.indexOf("com"), absolutePath.indexOf(".class"));
                    absolutePath = absolutePath.replace("\\", ".");
                    buildBeDifinition(classLoader, absolutePath);
                }
            }

        }
    }

    private void register(Map<String, BeanDefinition> beanDefinitionMap) {
        if (CollUtil.isEmpty(beanDefinitionMap)) return;
        //遍历所有的beanDefinition
        for (Map.Entry<String, BeanDefinition> map : beanDefinitionMap.entrySet()){
            String beanName = map.getKey();
            BeanDefinition beanDefinition = map.getValue();
            //如果是单例bean
            if (StrUtil.equals(beanDefinition.getScope(),SINGLETON)){
                //正在的创建单例bean
                Object bean = doCreateBean(beanName, beanDefinition);
                singletonObjects.put(beanName, bean);
            }
        }
    }

    private Object doCreateBean(String beanName, BeanDefinition beanDefinition) {
        Class clazz = beanDefinition.getType();

        Object object = null;
        try {
            object = clazz.getConstructor().newInstance();
            for (Field field : clazz.getDeclaredFields()) {
                if (field.isAnnotationPresent(Autowired.class)) {
                    field.setAccessible(true);
                    field.set(object, getObjectBean(field.getName()));
                }
            }
            if (object instanceof BeanNameAware) {
                ((BeanNameAware)object).setBeanName(beanName);
            }
            if (object instanceof InitializingBean) {
                ((InitializingBean)object).afterPropertiesSet();
            }
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                object = beanPostProcessor.postProcessBeforeInitialization(object, beanName);
            }
            for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) {
                object = beanPostProcessor.postProcessAfterInitialization(object, beanName);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return object;
    }

    //构建beanDefinition
    private void buildBeDifinition(ClassLoader classLoader, String absolutePath) {
        try{
            //通过类加载器加载
            Class<?> clazz = classLoader.loadClass(absolutePath);
            //判断类上面是否有注解
            if (!clazz.isAnnotationPresent(Component.class)) return;
            //是否有前置处理器
            if (BeanPostProcessor.class.isAssignableFrom(clazz)) {
                //通过构造方法获取bean的前置处理器
                BeanPostProcessor beanPostProcessor = (BeanPostProcessor) clazz.getConstructor().newInstance();
                beanPostProcessorList.add(beanPostProcessor);
            }

            //如果直接使用component注解，里面没有value值，则使用小写的形式设置beanName
            Component componentAnnotation = clazz.getAnnotation(Component.class);
            String beanName = componentAnnotation.value();
            if (StrUtil.equals(beanName,"")) {
                //参考spring源码里面的方式
                beanName = Introspector.decapitalize(clazz.getSimpleName());
            }

            BeanDefinition beanDefinition = new BeanDefinition();
            beanDefinition.setType(clazz);

            //判断是单例bean还是原型bean
            if (clazz.isAnnotationPresent(Scope.class)) {
                Scope scopeAnnotation = clazz.getAnnotation(Scope.class);
                String value = scopeAnnotation.value();
                beanDefinition.setScope(value);
            } else {
                beanDefinition.setScope(SINGLETON);
            }

            //最后将实例bean注册到三级缓存中
            beanDefinitionMap.put(beanName, beanDefinition);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public Object getObjectBean(String beanName) {

        if (!beanDefinitionMap.containsKey(beanName)) {
            throw new NullPointerException();
        }

        BeanDefinition beanDefinition = beanDefinitionMap.get(beanName);

        if (beanDefinition.getScope().equals("singleton")) {
            Object singletonBean = singletonObjects.get(beanName);
            if (singletonBean == null) {
                singletonBean = doCreateBean(beanName, beanDefinition);
                singletonObjects.put(beanName, singletonBean);
            }
            return singletonBean;
        } else {
            // 原型
            Object prototypeBean = doCreateBean(beanName, beanDefinition);
            return prototypeBean;
        }

    }

}
